import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;


/**
 * 
 */

/**
 * @author zero
 * 
 */
public class ColorBlocksApp
{

    public static Block[][] blockList = new Block[15][6];
    // public static int[][] blanks = new int[15][6];

    static
    {
        for (int i = 0; i < 15; i++)
        {
            for (int j = 0; j < 6; j++)
            {
                Block block = new Block();
                block.x = j;
                block.y = i;
                block.color = (int) (Math.random() * 4);
                blockList[block.y][block.x] = block;
            }
        }
    }

    public static void printBlocks(Block[][] blockList)
    {
        for (int y = 14; y >= 0; y--)
        {
            Block[] bList = blockList[y];
            for (int x = 0; x < 6; x++)
            {
                Block block = bList[x];
                System.out.print(block.color + " ");
            }

            System.out.println();

        }
    }

    public static void readFile(String path)
    {
        System.out.println("Reading file...");
        try
        {

            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    new FileInputStream(path)));

            String line = "";
            int y = 14;
            while ((line = reader.readLine()) != null)
            {
                // System.out.println("[" + line + "]");
                String[] colors = line.split(" ");

                for (int i = 0; i < 6; i++)
                {
                    blockList[y][i].color = Integer.valueOf(colors[i]);

                }

                y--;

            }

        }
        catch (FileNotFoundException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 
     */
    public static void main(String[] args)
    {
        // TODO Auto-generated method stub
        readFile("./blocks");
        printBlocks(blockList);
        SizeParam param = new SizeParam();
        SizeParam gSize = new SizeParam();
        Direction from = new Direction();

        param.minLeft = 0;
        param.minRight = 0;
        param.minTop = 0;
        param.minBottom = 0;
        CleanBlockList cleanList = new CleanBlockList();

        // makeBigger(blockList[9][3], param, gSize, from);
        blockList[9][3].cleanLevel = 1;
        blockList[9][3].isCanClear = true;
        cleanList.addBlock(blockList[9][3]);
        findBlockToClear(blockList[9][3], gSize, from, cleanList);

        Iterator<List<Block>> it = cleanList.iterator();
        while (it.hasNext())
        {
            List<Block> nextLevel = it.next();

            System.out.println("##########################");
            for (Block block : nextLevel)
            {
                block.color = 8;
            }

            printBlocks(blockList);
            System.out.println("##########################");
        }

        System.out.println("Min bottom: " + gSize.minBottom);
        System.out.println("Min top: " + gSize.minTop);
        System.out.println("Min left: " + gSize.minLeft);
        System.out.println("Min right: " + gSize.minRight);

    }

    public static class Direction implements Cloneable
    {
        public static final Direction LEFT  = new Direction(true, false, false,
                                                    false);
        public static final Direction RIGHT = new Direction(false, true, false,
                                                    false);
        public static final Direction DOWN  = new Direction(false, false, true,
                                                    false);
        public static final Direction UP    = new Direction(false, false,
                                                    false, true);

        public boolean                left;
        public boolean                right;
        public boolean                up;
        public boolean                down;

        public Direction()
        {
        }

        private Direction(boolean left, boolean right, boolean down, boolean up)
        {
            this.left = left;
            this.right = right;
            this.down = down;
            this.up = up;
        }

        @Override
        public Object clone()
        {
            try
            {
                return super.clone();
            }
            catch (CloneNotSupportedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    }

    public static class SizeParam implements Cloneable
    {
        public int maxLeft   = 0;
        public int minLeft   = 15;
        public int maxRight  = 0;
        public int minRight  = 15;
        public int maxTop    = 0;
        public int minTop    = 15;
        public int maxBottom = 0;
        public int minBottom = 15;
        public int level     = 0;

        @Override
        public Object clone()
        {
            try
            {
                return super.clone();
            }
            catch (CloneNotSupportedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return null;
        }
    }

    public void scanBigger(Block block)
    {
        // SizeParam sizeParam = new SizeParam();
        //
        // int startX = block.x;
        // int startY = block.y;
        // int color = block.color;
        //
        // int offsetHigh = 1;
        //
        // // to up
        // Block nextBlock = blockList[startX][startY + offsetHigh];
        // while (null != nextBlock && nextBlock.color == color)
        // {
        // sizeParam.maxH++;
        // sizeParam.minH++;
        // ++offsetHigh;
        // nextBlock = blockList[startX][startY + offsetHigh];
        // }
        //
        // // to down
        // int offsetLow = -1;
        // nextBlock = blockList[startX][startY + offsetLow];
        // while (null != nextBlock && nextBlock.color == color)
        // {
        // sizeParam.maxH++;
        // sizeParam.minH++;
        // --offsetLow;
        // nextBlock = blockList[startX][startY + offsetLow];
        // }
        //
        // // to left
        // int offsetLeft = -1;
        // nextBlock = blockList[startX + offsetLeft][startY];
        // while (null != nextBlock && nextBlock.color == color)
        // {
        // sizeParam.maxW++;
        // sizeParam.minW++;
        // --offsetLeft;
        // nextBlock = blockList[startX + offsetLeft][startY];
        // }
        //
        // // to right
        // int offsetRight = 1;
        // nextBlock = blockList[startX + offsetRight][startY];
        // while (null != nextBlock && nextBlock.color == color)
        // {
        // sizeParam.maxW++;
        // sizeParam.minW++;
        // ++offsetLeft;
        // nextBlock = blockList[startX + offsetRight][startY];
        // }
        //        
        // int offsetX = startX + offsetLeft;
        // nextBlock = blockList[offsetX][startY];
        // for(;(null !=nextBlock) && nextBlock.color == color; ++offsetX)
        // {
        //            
        // }

    }

    public static void makeBigger(Block block, SizeParam sizeParam,
            SizeParam gSizeParam, Direction from)
    {
        System.out.println("Making bigger...");
        int startX = block.x;
        int startY = block.y;
        int color = block.color;
        SizeParam curSizeParam = (SizeParam) sizeParam.clone();
        //

        // left
        if (startX > 0 && !from.right)
        {
            Block nextBlock = blockList[startY][startX - 1];
            if (null == nextBlock)
                return;
            else if (nextBlock.color == color)
            {
                curSizeParam.maxLeft++;
                curSizeParam.minLeft++;
                if (curSizeParam.maxLeft > gSizeParam.maxLeft)
                {
                    gSizeParam.maxLeft = curSizeParam.maxLeft;
                }
                from.left = true;
                makeBigger(nextBlock, curSizeParam, gSizeParam, from);
            }
            else
            {
                if (curSizeParam.minLeft < gSizeParam.minLeft)
                {
                    gSizeParam.minLeft = curSizeParam.minLeft;
                }
                return;
            }
        }
        // right
        if (startX < 5 && !from.left)
        {
            Block nextBlock = blockList[startY][startX + 1];
            if (null == nextBlock)
                return;
            else if (nextBlock.color == color)
            {
                curSizeParam.maxRight++;
                curSizeParam.minRight++;
                if (curSizeParam.maxRight > gSizeParam.maxRight)
                {
                    gSizeParam.maxRight = curSizeParam.maxRight;
                }
                from.right = true;
                makeBigger(nextBlock, curSizeParam, gSizeParam, from);
            }
            else
            {
                if (curSizeParam.minRight < gSizeParam.minRight)
                {
                    gSizeParam.minRight = curSizeParam.minRight;
                }
                return;
            }
        }

        // down
        if (startY > 0 && !from.up)
        {
            Block nextBlock = blockList[startY - 1][startX];
            if (null == nextBlock)
                return;
            else if (nextBlock.color == color)
            {
                curSizeParam.maxBottom++;
                curSizeParam.minBottom++;
                if (curSizeParam.maxBottom > gSizeParam.maxBottom)
                {
                    gSizeParam.maxBottom = curSizeParam.maxBottom;
                }
                from.down = true;
                makeBigger(nextBlock, curSizeParam, gSizeParam, from);
            }
            else
            {
                if (curSizeParam.minBottom < gSizeParam.minBottom)
                {
                    gSizeParam.minBottom = curSizeParam.minBottom;
                }
                return;
            }
        }
        // up
        if (startY < 14 && !from.down)
        {
            Block nextBlock = blockList[startY + 1][startX];
            if (null == nextBlock)
                return;
            else if (nextBlock.color == color)
            {
                curSizeParam.maxTop++;
                curSizeParam.minTop++;
                if (curSizeParam.maxTop > gSizeParam.maxTop)
                {
                    gSizeParam.maxTop = curSizeParam.maxTop;
                }
                from.up = true;
                makeBigger(nextBlock, curSizeParam, gSizeParam, from);
            }
            else
            {
                if (curSizeParam.minTop < gSizeParam.minTop)
                {
                    gSizeParam.minTop = curSizeParam.minTop;
                }
                return;
            }
        }

    }

    private static boolean isCanClear(Block block, int color)
    {
        return null != block && block.color == color && !block.isCanClear;
    }

    private static void setBlockClear(Block block, int level,
            CleanBlockList cleanBlockList)
    {
        block.cleanLevel = level;
        block.isCanClear = true;
        cleanBlockList.addBlock(block);

    }

    public static void findBlockToClear(Block block, SizeParam gSizeParam,
            Direction from, CleanBlockList cleanBlockList)
    {
        System.out.println("Finding blocks to clear...");
        List<Block> retList = new LinkedList<Block>();

        int startX = block.x;
        int startY = block.y;
        if (startX == 3 && startY == 10)
        {
            System.out.println("");
        }
        int color = block.color;
        int level = block.cleanLevel + 1;

        Direction triedFrom = (Direction) from.clone();

        // left
        if (startX > 0 && !triedFrom.right)
        {
            Block nextBlock = blockList[startY][startX - 1];
            if (isCanClear(nextBlock, color))
            {
                Direction nextFrom = (Direction) triedFrom.clone();
                nextFrom.left = true;
                setBlockClear(nextBlock, level, cleanBlockList);
                findBlockToClear(nextBlock, gSizeParam, nextFrom,
                                 cleanBlockList);
            }
        }
        // right
        if (startX < 5 && !triedFrom.left)
        {
            Block nextBlock = blockList[startY][startX + 1];
            if (isCanClear(nextBlock, color))
            {
                Direction nextFrom = (Direction) triedFrom.clone();
                nextFrom.right = true;
                setBlockClear(nextBlock, level, cleanBlockList);
                findBlockToClear(nextBlock, gSizeParam, nextFrom,
                                 cleanBlockList);
            }
        }

        // down
        if (startY > 0 && !triedFrom.up)
        {
            Block nextBlock = blockList[startY - 1][startX];
            if (isCanClear(nextBlock, color))
            {
                Direction nextFrom = (Direction) triedFrom.clone();
                nextFrom.down = true;
                setBlockClear(nextBlock, level, cleanBlockList);
                findBlockToClear(nextBlock, gSizeParam, nextFrom,
                                 cleanBlockList);
            }
        }
        // up
        if (startY < 14 && !triedFrom.down)
        {
            Block nextBlock = blockList[startY + 1][startX];
            if (isCanClear(nextBlock, color))
            {
                Direction nextFrom = (Direction) triedFrom.clone();
                nextFrom.up = true;
                setBlockClear(nextBlock, level, cleanBlockList);
                findBlockToClear(nextBlock, gSizeParam, nextFrom,
                                 cleanBlockList);
            }
        }
        return;

    }
}
